
///
/// This is the implementation of the Client class
///
#include "stdlib.h"
#include <iostream>
#include <netdb.h>
#include <arpa/inet.h>
#include <cstring>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <cassert>
#include "common.h"
#include "Handler.h"
#include "Client.h"

Client* Client::mClient = NULL;

int Client::Initialize(const char* ip, const char* portNo, const char* uri)
{
	struct addrinfo temp, *res;

	memset(&temp, 0, sizeof(temp));		
	temp.ai_family = AF_INET;		
	temp.ai_socktype = SOCKET_TYPE;
	
	int retVal;

	if((retVal = getaddrinfo(ip, portNo, &temp, &res)) != 0)
	{
		std::cout << "getaddrinfo() error: " << gai_strerror(retVal) << std::endl;
		exit(1);
	}

	std::cout << "Connecting to server..." << std::endl;
	std::cout << "IP Address: " << ip << std::endl;
	std::cout << "Port Number: " << portNo << std::endl;
	std::cout << "URI: " << uri << std::endl;


	//create client socket
	mMainFD = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
	if(mMainFD == -1)
	{
		perror("socket() error: ");
		exit(2);
	}

	if(connect(mMainFD, res->ai_addr, res->ai_addrlen) == -1)
	{
		perror("connect() error: ");
		close(mMainFD);
		exit(2);
	}

	std::cout << "Connected successfully to server!" << std::endl;

	freeaddrinfo(res);

	mURI = std::string(uri);

	mMsgHandler = NULL;
	
	return 0;
}

int Client::Execute()
{
	//initialize FD set for select()
	fd_set readFDS;
	FD_ZERO(&readFDS);
	FD_ZERO(&mConnectedFDS);
	
	//FD_SET(STDIN, &mConnectedFDS);
	FD_SET(mMainFD, &mConnectedFDS);
	mMaxFD = mMainFD; //std::max(STDIN, mMainFD);

	mMsgHandler = new Handler(mMainFD, CLIENT);

	if(SendGet() == -1)
	{
		std::cout << "Couldn't send GET... exiting!" << std::endl;
		close(mMainFD);
		exit(2);
	}

	struct timeval tv;

	int retVal;

	while(1)
	{
		readFDS = mConnectedFDS;
		tv.tv_sec = 300;	tv.tv_usec = 0; // 5 min timeout
		retVal = select(mMaxFD+1, &readFDS, NULL, NULL, &tv);
		if(retVal == -1)
		{
			perror("select() error: ");
			exit(3);
		}
		else if(retVal == 0)
		{
			std::cout << "HTTP Request Timed Out! Exiting..." << std::endl;
			return -1;
		}

		for(int i = 0; i <= mMaxFD; ++i)
		{
			if(FD_ISSET(i, &readFDS))
			{
				if(i == STDIN)
				{
#if 0
					//handle std input commands
					static char chatBuf[MSG_BUF_SIZE];
					if(!fgets(chatBuf, MSG_BUF_SIZE, stdin))
					{
						if(ferror(stdin))
						{
							perror("fgets(stdin) error: ");
							close(mMainFD);
							return 3;
						}
					}

					static const std::string quitStr("QUIT\n");
					if(quitStr.compare(chatBuf) == 0)
					{
						//QUIT received from user, send LEAVE and close socket
						SendLeave();
						close(mMainFD);
						return 0;
					}

					//SEND chat to server
					if(SendChat(chatBuf) == -1)
					{
						std::cout << "Failed to send chat msg to server" << std::endl;
						close(mMainFD);
						exit(3);
					}
#endif
				}
				else if(i == mMainFD)
				{

					int byteCount = recv(mMainFD, mMsgBuf, MAX_CONTENT_BUF_SIZE, 0);
					
					if(byteCount == -1)
					{
						perror("recv() error: ");
						close(mMainFD);
						exit(3);
					}
					else if(byteCount == 0)
					{
						std::cout << "Server closed the socket!" << std::endl;
						close(mMainFD);
						exit(3);
					}
					
					/*char* msg = &mMsgBuf[0];
					for(int i = 0; i < byteCount; ++i)
					{
						std::cout << *msg;
						++msg;
					}
					close(mMainFD); return 0;*/
					if(mMsgHandler->HandleMessage(mMsgBuf, byteCount) == -1)
					{
						std::cout << "Client couldn't interpret the message" << std::endl;
						continue;
					}
				}
				else
				{
					assert(false);
				}
			}
		}
	}

	close(mMainFD);
	return 0;
}

int Client::SendGet()
{
	sprintf(mMsgBuf, "GET %s HTTP/1.0%s", mURI.c_str(), DOUBLE_CRLF);

	std::cout << "Sending HTTP GET message:" << std::endl << mMsgBuf 
		<< std::endl;

	return send(mMainFD, mMsgBuf, strlen(mMsgBuf), 0);
}

int Client::Die()
{
	if(mMsgHandler != NULL)
		delete mMsgHandler;
	close(mMainFD);
	exit(0);
}
